package com.qm.console.pubdef.dto.bx;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.qm.console.component.exception.BusinessException;
import com.qm.console.controller.dto.ResultEnum;
import com.qm.console.dao.mapper.BxDictDOMapper;
import com.qm.console.dao.model.BxAtomFieldDO;
import com.qm.console.dao.model.BxDictDO;
import com.qm.console.dao.model.BxDictDOExample;
import com.qm.console.pubdef.constant.bx.BxAggregateMethod;
import com.qm.console.pubdef.constant.bx.BxLogicMethod;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lombok.Getter;
import lombok.Setter;

/**
 * //                       _oo0oo_
 * //                      o8888888o
 * //                      88" . "88
 * //                      (| -_- |)
 * //                      0\  =  /0
 * //                    ___/`---'\___
 * //                  .' \\|     |// '.
 * //                 / \\|||  :  |||// \
 * //                / _||||| -:- |||||- \
 * //               |   | \\\  -  /// |   |
 * //               | \_|  ''\---/''  |_/ |
 * //               \  .-\__  '-'  ___/-. /
 * //             ___'. .'  /--.--\  `. .'___
 * //          ."" '<  `.___\_<|>_/___.' >' "".
 * //         | | :  `- \`.;`\ _ /`;.`/ - ` : | |
 * //         \  \ `_.   \_ __\ /__ _/   .-` /  /
 * //     =====`-.____`.___ \_____/___.-`___.-'=====
 * //                       `=---='
 * //
 * //
 * //     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * //
 * //               佛祖保佑         永无BUG
 * <p>
 * com.qm.console.pubdef.dto.bx
 * Created on 2021/1/8.
 */
@Getter
@Setter
public class BxAtomFieldDTO {

    private Map<String, Object> relyOn;
    private Map<String, Object> conditions;
    private Integer id;
    private String fieldName;
    private String desc;
    private String formula;
    private Integer dateOffset;
    private Boolean addDefaultDisplayField;

    public static BxAtomFieldDTO fromDO(BxAtomFieldDO src) {
        BxAtomFieldDTO result = new BxAtomFieldDTO();
        BeanUtils.copyProperties(src, result);
        if (!StringUtils.isEmpty(src.getRelyOn()))
            result.setRelyOn(JSON.parseObject(src.getRelyOn(), new TypeReference<Map<String, Object>>() {
            }.getType()));
        if (!StringUtils.isEmpty(src.getConditions()))
            result.setConditions(JSON.parseObject(src.getConditions(), new TypeReference<Map<String, Object>>() {
            }.getType()));
        return result;
    }

    public BxAtomFieldDO toDO() {
        BxAtomFieldDO result = new BxAtomFieldDO();
        BeanUtils.copyProperties(this, result);
        if (relyOn != null) result.setRelyOn(JSON.toJSONString(relyOn));
        if (conditions != null) result.setConditions(JSON.toJSONString(conditions));
        return result;
    }


    public void validate(BxDictDOMapper bxDictDOMapper) {
        if (relyOn == null) {
            throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, "no rely on fields");
        }
        Set<String> fieldNames = new HashSet<>();
        for (Map.Entry<String, Object> entry : relyOn.entrySet()) {
            if (!(entry.getValue() instanceof String)) {
                throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("user field: %s aggregate method: %s is not string", entry.getKey(), entry.getValue()));
            }
            String method = BxAggregateMethod.fromMethod(entry.getValue().toString());
            if (method == null) {
                throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("aggregate method %s not supported", entry.getValue()));
            }
            fieldNames.add(entry.getKey());
        }
        BxDictDOExample example = new BxDictDOExample();
        example.createCriteria().andMongoFieldIn(new ArrayList<>(fieldNames));
        List<BxDictDO> dictItem = bxDictDOMapper.selectByExample(example);
        if (dictItem.size() != fieldNames.size()) {
            throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, "field name not in dict!");
        }
        //检查condition.
        this.checkCondition(bxDictDOMapper, conditions);
        //检查formula
        if (StringUtils.isEmpty(formula)) {
            throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, "formula is empty!");
        }
    }

    private void checkCondition(BxDictDOMapper bxDictDOMapper, Map<String, Object> conditions) {
        if (conditions == null) {
            return;
        }
        if (isGrouper(conditions)) {
            for (Map.Entry<String, Object> entry : conditions.entrySet()) {
                if (!BxLogicMethod.isLogicMethod(entry.getKey())) {
                    throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("%s is not a logic operator", entry.getKey()));
                }
                if (entry.getValue() instanceof List) {
                    for (Iterator<?> it = ((List<?>) entry.getValue()).iterator(); it.hasNext(); ) {
                        Object obj = it.next();
                        if (!(obj instanceof Map)) {
                            throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("%s is not a dict", obj));
                        }
                        checkCondition(bxDictDOMapper, (Map<String, Object>) obj);
                    }
                    return;
                }
                throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("%s is not a list with logic operation", entry.getValue()));
            }
        } else {
            if (!conditions.containsKey("field")) {
                throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("%s has no field specified", conditions));
            }
            if (conditions.containsKey("comparator")) {
                if (!BxLogicMethod.isComparator(String.valueOf(conditions.get("comparator")))) {
                    throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("%s is not a valid comparator", conditions.get("comparator")));
                }
            }
            BxDictDOExample example = new BxDictDOExample();
            example.createCriteria().andMongoFieldEqualTo(String.valueOf(conditions.get("field")));
            int count = bxDictDOMapper.countByExample(example);
            if (count <= 0) {
                throw new BusinessException(ResultEnum.BX_ATOM_VALIDATE_FAILED, String.format("%s is not a valid field", conditions.get("field")));
            }
        }
    }

    private boolean isGrouper(Map<String, Object> conditions) {
        if (conditions == null) {
            return false;
        }
        for (String k : conditions.keySet()) {
            if (k.contains("$")) {
                return true;
            }
        }
        return false;
    }
}
